﻿/*
 * This software is the confidential and proprietary
 * information of NetBay
 * Such confidential information should not be disclosed
 * NetBay copyright(c) 2010
 */

#region Using
using System;
using System.IO;
using System.Diagnostics;
using System.Reflection;

#endregion

namespace NetBay.Core.Log.Specific.Log4Net
{
    public class Log4NetLogger : ILogger
    {
        #region Constants
        /// <summary>
        /// Represents the lenght of member information trace
        /// </summary>
        private const int m_iPadRightMemberInfo = 92;
        #endregion

        private static string configFullFilePath;
        /// <summary>
        /// Gets or sets the config path.
        /// </summary>
        /// <value>The config path.</value>
        public static string ConfigFullFilePath
        {
            get
            {
                if (String.IsNullOrEmpty(configFullFilePath))
                {
                    var path = AppDomain.CurrentDomain.BaseDirectory;
                    configFullFilePath = Path.Combine(path, @"log4net.config");
                }
                return configFullFilePath;
            }
            set
            {
                if (String.IsNullOrEmpty(configFullFilePath))
                {
                    string path = AppDomain.CurrentDomain.BaseDirectory;
                    configFullFilePath = Path.Combine(path, @"Log4Net.config");
                }
                configFullFilePath = value;
                LoadConfig();
            }
        }

        /// <summary>
        /// Use to get class name when using a logging
        /// </summary>
        private static int logCall = 0;

        public static void LoadConfig()
        {
            if (File.Exists(configFullFilePath))
            {
                var file = new FileInfo(configFullFilePath);
                log4net.Config.XmlConfigurator.ConfigureAndWatch(file);
                file = null;
            }
            else
            {
                //Log Framework, No configuration file define, use application configuration file
                log4net.Config.XmlConfigurator.Configure();
            }
        }

        private log4net.ILog logger;
        /// <summary>
        /// Initializes a new instance of the <see cref="Log4NetLogger"/> class.
        /// Set 'logger' as current logger
        /// </summary>
        /// <param name="logger">The logger.</param>
        public Log4NetLogger(log4net.ILog logger)
        {
            this.logger = logger;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Log4NetLogger"/> class.
        /// Set a logger name 'loggerName' as current logger
        /// </summary>
        /// <param name="loggerName">Name of the logger.</param>
        public Log4NetLogger(string loggerName)
        {
            this.logger = log4net.LogManager.GetLogger(loggerName);
        }

        /// <summary>
        /// Defines the properties.
        /// </summary>
        private void DefineProperties()
        {
            try
            {
                DefineClassProperties();
            }
            catch (TypeInitializationException ex)
            {
                this.logger.Error("Define properties in logger failed", ex);
            }
        }

        /// <summary>
        /// Defines the calling class the properties.
        /// </summary>
        private static void DefineClassProperties()
        {
            var stackTrace = new StackTrace();
            if (stackTrace.FrameCount > 3 + logCall)
            {
                MethodBase methodBase = stackTrace.GetFrame(3 + logCall).GetMethod();
                string className = string.Empty;
                if (methodBase.DeclaringType == null)
                {
                    className = "Error in methodBase.DeclaringType. The object is null";
                }
                else
                {
                    className = methodBase.DeclaringType.FullName;
                }

                
                string methodName = methodBase.Name;
                log4net.GlobalContext.Properties["ClassName"] = className;
                log4net.GlobalContext.Properties["MethodName"] = methodName;
            }
        }

        /// <summary>
        /// Retreive Full qualified name of function call
        /// </summary>
        /// <returns>Full Qualified Name</returns>
        private static string GetFullQualifiedName()
        {
            try
            {
                StackFrame frame = new StackFrame(3);
                MethodBase method = frame.GetMethod();
                return method.ReflectedType.FullName + "." + method.Name;
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Retreive the full qualified name of function call with a formatage for log
        /// </summary>
        /// <returns>string to log</returns>
        private static string GetHeaderFunction()
        {
            return GetFullQualifiedName().PadRight(m_iPadRightMemberInfo) + "| ";
        }

        #region ILogger Members

        #region LogDebug
        public void LogDebug(string logString)
        {
            logCall++;
            internalLogDebugFormat("{0}", GetHeaderFunction() + logString);
        }
        public void LogDebugFormat(string logFormat, params object[] args)
        {
            logCall++;
            internalLogDebugFormat(GetHeaderFunction() + logFormat, args);
        }
        private void internalLogDebugFormat(string logFormat, params object[] args)
        {
            logCall++;
            LogDebugFormat(null, logFormat, args);
        }

        /// <summary>
        /// Logs the debug format.
        /// </summary>
        /// <param name="ex">The exception</param>
        /// <param name="logFormat">The log format.</param>
        /// <param name="args">The args.</param>
        public void LogDebugFormat(Exception ex, string logFormat, params object[] args)
        {
            if (this.logger.IsDebugEnabled)
            {
                DefineProperties();
                if (ex != null)
                {
                    this.logger.Debug(string.Format(logFormat, args), ex);
                }
                else
                {
                    this.logger.DebugFormat(logFormat, args);
                }
            }
            logCall = 0;
        }
        #endregion

        #region LogError
        public void LogError(string logString)
        {
            logCall++;
            internalLogErrorFormat("{0}", GetHeaderFunction() + logString);
        }
        public void LogErrorFormat(string logFormat, params object[] args)
        {
            logCall++;
            internalLogErrorFormat(GetHeaderFunction() + logFormat, args);
        }
        private void internalLogErrorFormat(string logFormat, params object[] args)
        {
            logCall++;
            LogErrorFormat(null, logFormat, args);
        }

        public void LogErrorFormat(Exception ex, string logFormat, params object[] args)
        {
            if (logger.IsErrorEnabled)
            {
                DefineProperties();
                if (ex != null)
                {
                    this.logger.Error(string.Format(logFormat, args), ex);
                }
                else
                {
                    this.logger.ErrorFormat(logFormat, args);
                }
            }
            logCall = 0;
        }
        #endregion

        #region LogFatal
        public void LogFatal(string logString)
        {
            logCall++;
            LogFatalFormat("{0}", GetHeaderFunction() + logString);
        }
        public void LogFatalFormat(string logFormat, params object[] args)
        {
            logCall++;
            LogFatalFormat(null, logFormat, args);
        }
        public void LogFatalFormat(Exception ex, string logFormat, params object[] args)
        {
            if (logger.IsFatalEnabled)
            {
                DefineProperties();
                if (ex != null)
                {
                    this.logger.Fatal(string.Format(logFormat, args), ex);
                }
                else
                {
                    this.logger.FatalFormat(logFormat, args);
                }
            }
            logCall = 0;
        }
        #endregion

        #region LogInfo
        public void LogInfo(string logString)
        {
            logCall++;
            internalLogInfoFormat("{0}", GetHeaderFunction() + logString);
        }
        public void LogInfoFormat(string logFormat, params object[] args)
        {
            logCall++;
            internalLogInfoFormat(GetHeaderFunction() + logFormat, args);
        }
        private void internalLogInfoFormat(string logFormat, params object[] args)
        {
            logCall++;
            LogInfoFormat(null, logFormat, args);
        }
        public void LogInfoFormat(Exception ex, string logFormat, params object[] args)
        {
            if (logger.IsInfoEnabled)
            {
                DefineProperties();
                if (ex != null)
                {
                    this.logger.Info(string.Format(logFormat, args), ex);
                }
                else
                {
                    this.logger.InfoFormat(logFormat, args);
                }
            }
            logCall = 0;
        }
        #endregion

        #region LogWarn
        public void LogWarn(string logString)
        {
            logCall++;
            LogWarnFormat("{0}", GetHeaderFunction() + logString);
        }
        public void LogWarnFormat(string logFormat, params object[] args)
        {
            logCall++;
            LogWarnFormat(null, logFormat, args);
        }
        public void LogWarnFormat(Exception ex, string logFormat, params object[] args)
        {
            DefineProperties();
            if (logger.IsWarnEnabled)
            {
                DefineProperties();
                if (ex != null)
                {
                    this.logger.Warn(string.Format(logFormat, args), ex);
                }
                else
                {
                    this.logger.WarnFormat(logFormat, args);
                }
            }
            logCall = 0;
        }
        #endregion

        #endregion
    }
}
